package com.xiaoshuidi.cloud.module.rooms.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.xiaoshuidi.cloud.framework.common.exception.ErrorCode;
import com.xiaoshuidi.cloud.framework.common.exception.ServiceException;
import com.xiaoshuidi.cloud.framework.common.pojo.CommonResult;
import com.xiaoshuidi.cloud.framework.common.pojo.PageResult;
import com.xiaoshuidi.cloud.framework.common.util.json.JsonUtils;
import com.xiaoshuidi.cloud.module.rooms.api.home.vo.RoomLayoutVO;
import com.xiaoshuidi.cloud.module.rooms.controller.app.home.vo.*;
import com.xiaoshuidi.cloud.module.rooms.enums.ApiConstants;
import com.xiaoshuidi.cloud.module.rooms.enums.RoomTypeEnum;
import com.xiaoshuidi.cloud.module.rooms.enums.TowardEnum;
import com.xiaoshuidi.cloud.module.rooms.enums.apphome.SortRuleEnum;
import com.xiaoshuidi.cloud.module.rooms.framework.config.ConfigValues;
import com.xiaoshuidi.cloud.module.rooms.framework.utils.BaiDuMapUtil;
import com.xiaoshuidi.cloud.module.rooms.framework.utils.GaoDeUtil;
import com.xiaoshuidi.cloud.module.rooms.framework.utils.RoomHouseUtil;
import com.xiaoshuidi.cloud.module.rooms.pojo.*;
import com.xiaoshuidi.cloud.module.rooms.pojo.roomel.RoomelEntity;
import com.xiaoshuidi.cloud.module.rooms.repository.*;
import com.xiaoshuidi.cloud.module.rooms.repository.elsticsearch.RoomelEntityRepository;
import com.xiaoshuidi.cloud.module.rooms.service.HomeService;
import com.xiaoshuidi.cloud.module.rooms.utils.NumberUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.BucketOrder;
import org.elasticsearch.search.aggregations.PipelineAggregatorBuilders;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedTerms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.aggregations.metrics.*;
import org.elasticsearch.search.aggregations.pipeline.BucketSortPipelineAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.stereotype.Service;
import org.zxp.esclientrhl.enums.Analyzer;
import org.zxp.esclientrhl.repository.ElasticsearchTemplate;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors;

import static com.xiaoshuidi.cloud.module.rooms.pojo.roomel.RoomelEntityMapping.IsStop;

/**
 * @author hans.zhang
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class HomeServiceImpl implements HomeService {

    private final ElasticsearchTemplate elasticsearchTemplate;
    private final ApartmentRepository apartmentRepository;
    private final RoomHouseRepository roomHouseRepository;
    private final ConfigValues configValues;
    private final RoomLayoutRepository roomLayoutRepository;
    private final RoomHouseTypeRepository roomHouseTypeRepository;
    private final RoomApartmentAreaRepository roomApartmentAreaRepository;
    private final GaoDeUtil gaoDeUtil;
    private final RoomHouseFilingsRepository roomHouseFilingsRepository;
    private final RoomelEntityRepository roomelEntityRepository;
    private final BaiDuMapUtil baiDuMapUtil;

    /**
     * 集中式-搜索找房
     *
     * @param searchBox        搜索框查询
     * @param district         区域
     * @param monthRentalMin   租金范围-下限
     * @param monthRentalMax   租金范围-上限
     * @param longitude        地铁站经度
     * @param latitude         地铁站纬度
     * @param sortRule         排序规则 1:推荐排序 2:最新发布 3:价格升序 4:价格降序 5:面积升序 6:面积降序
     * @param isWhole          出租方式,是否整租 true:整租 false:合租
     * @param bedRoomNum       户型-几室 1:一室 2:二室 3:三室 4:四室 5:其他
     * @param publicFacilities 特色需求(公共设施)
     * @param tenantId
     * @param lon              用户所在经度
     * @param lat              用户所在纬度
     * @return 门店信息
     */
    @Override
    public CommonResult<PageResult<ConcentratedFindHouseRespVO>> concentratedFindHouse(String searchBox, String district, Double monthRentalMin,
                                                                                       Double monthRentalMax, Double longitude, Double latitude, Integer sortRule, Boolean isWhole,
                                                                                       Integer bedRoomNum, String publicFacilities, Integer pageNum, Integer pageSize, Long tenantId, String lon, String lat) {
        log.info("集中式-搜索找房接口调用searchBox:[{}], district:[{}],  monthRentalMin:[{}],  monthRentalMax:[{}],  longitude:[{}],  " +
                        "latitude:[{}],  sortRule:[{}],  isWhole:[{}],  bedRoomNum:[{}],  publicFacilities:[{}],  pageNum:[{}],  pageSize:[{}],  tenantId:[{}], lon:[{}], lat:[{}] ",
                searchBox, district, monthRentalMin, monthRentalMax, longitude, latitude, sortRule, isWhole, bedRoomNum, publicFacilities, pageNum, pageSize, tenantId, lon, lat);
        // 原生查询请求
        SearchRequest searchRequest = new SearchRequest(ApiConstants.ROOMELENTIRY);
        // 查询条件构建
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery()
                .must(QueryBuilders.termQuery("tenantId", tenantId))
                .must(QueryBuilders.termQuery(IsStop, false))
                .must(QueryBuilders.termQuery("isConcentrated", true))
                .mustNot(QueryBuilders.existsQuery("superId"));
        if (ObjectUtil.isNotEmpty(searchBox)) {
            BoolQueryBuilder shouldQueryBuilder = QueryBuilders.boolQuery()
                    .should(QueryBuilders.wildcardQuery("district.keyword", "*" + searchBox + "*"))
                    .should(QueryBuilders.wildcardQuery("street.keyword", "*" + searchBox + "*"))
                    .should(QueryBuilders.multiMatchQuery(searchBox, "apartmentName", "address").analyzer(Analyzer.ik_max_word.name()));
            boolQueryBuilder.must(shouldQueryBuilder);
        }
        // 区域
        if (ObjectUtil.isNotEmpty(district)) {
            boolQueryBuilder.must(QueryBuilders.termQuery("district", district));
        }

        // 价格区间
        if (ObjectUtil.isAllNotEmpty(monthRentalMin, monthRentalMax)) {
            boolQueryBuilder.must(QueryBuilders.rangeQuery("price").gte(monthRentalMin).lte(monthRentalMax));
        } else if (ObjectUtil.isNotEmpty(monthRentalMin) && ObjectUtil.isEmpty(monthRentalMax)) {
            boolQueryBuilder.must(QueryBuilders.rangeQuery("price").gte(monthRentalMin));
        }

        // 地理位置
        if (ObjectUtil.isAllNotEmpty(longitude, latitude)) {
            boolQueryBuilder.must(QueryBuilders.geoDistanceQuery("apartmentGeo").distance("2000").point(latitude, longitude));
        }

        // 出租方式
        if (ObjectUtil.isNotEmpty(isWhole)) {
            boolQueryBuilder.must(QueryBuilders.termQuery("isWhole", isWhole));
        }

        // 几室
        if (ObjectUtil.isNotEmpty(bedRoomNum)) {
            boolQueryBuilder.must(QueryBuilders.termQuery("bedroomNum", bedRoomNum));
        }

        // 特色需求
        if (ObjectUtil.isNotEmpty(publicFacilities)) {
            String[] split = publicFacilities.split(",");
            BoolQueryBuilder shouldBoolQueryBuilder = QueryBuilders.boolQuery();
            for (String s : split) {
                shouldBoolQueryBuilder.should(QueryBuilders.matchQuery("publicFacilities", s));
            }
            boolQueryBuilder.must(shouldBoolQueryBuilder);
        }

        // 按门店分组
        TermsAggregationBuilder apartmentGroupAgg = AggregationBuilders.terms("apartmentGroup").field("apartmentId").size(10000);
        // select 的字段
        TopHitsAggregationBuilder apartmentInfoTopHits = AggregationBuilders.topHits("apartmentInfo")
                .fetchSource(new String[]{"id", "apartmentId", "apartmentName", "price", "apartmentGeo"}, null).size(1);

        // 桶排序，并对桶分页
        List<FieldSortBuilder> sortList = new ArrayList<>();
        // 获取分组后的桶数量
        CardinalityAggregationBuilder cardinalityAggregationBuilder = AggregationBuilders.cardinality("bucketCount").field("apartmentId");

        MinAggregationBuilder priceMinAgg = AggregationBuilders.min("priceMin").field("price");
        apartmentGroupAgg.subAggregation(priceMinAgg);
        MaxAggregationBuilder spaceMaxAgg = AggregationBuilders.max("spaceMax").field("space");
        apartmentGroupAgg.subAggregation(spaceMaxAgg);
        // 排序规则
        if (SortRuleEnum.TJ.getValue().equals(sortRule)) {
            // 推荐排序， 按剩余空房数量
            boolQueryBuilder.must(QueryBuilders.termQuery("houseState", "1"));
            apartmentGroupAgg.order(BucketOrder.count(false));
        } else if (SortRuleEnum.ZX.getValue().equals(sortRule)) {
            // 按最新发布房源排序
            apartmentInfoTopHits.sort("createTime", SortOrder.DESC);
            MaxAggregationBuilder maxAggregationBuilder = AggregationBuilders.max("maxSort").field("createTime");
            apartmentGroupAgg.subAggregation(maxAggregationBuilder);
            sortList.add(new FieldSortBuilder("maxSort").order(SortOrder.DESC));
        } else if (SortRuleEnum.JGSX.getValue().equals(sortRule)) {
            // 房源价格升序
            sortList.add(new FieldSortBuilder("priceMin").order(SortOrder.ASC));
        } else if (SortRuleEnum.JGJX.getValue().equals(sortRule)) {
            // 房源价格降序
            sortList.add(new FieldSortBuilder("priceMin").order(SortOrder.DESC));
        } else if (SortRuleEnum.MJSX.getValue().equals(sortRule)) {
            // 房源面积升序
            sortList.add(new FieldSortBuilder("spaceMax").order(SortOrder.ASC));
        } else if (SortRuleEnum.MJJX.getValue().equals(sortRule)) {
            // 房源面积降序
            sortList.add(new FieldSortBuilder("spaceMax").order(SortOrder.DESC));
        }
        // select
        apartmentGroupAgg.subAggregation(apartmentInfoTopHits);
        // 桶排序和分页
        BucketSortPipelineAggregationBuilder bucketSort = PipelineAggregatorBuilders.bucketSort("testSort", sortList).from((pageNum - 1) * pageSize).size(pageSize);
        apartmentGroupAgg.subAggregation(bucketSort);
        searchSourceBuilder.aggregation(apartmentGroupAgg);
        // 桶数量
        searchSourceBuilder.aggregation(cardinalityAggregationBuilder);
        // 查询条件
        searchSourceBuilder.query(boolQueryBuilder);
        searchRequest.source(searchSourceBuilder);
        searchSourceBuilder.size(0);

        SearchResponse searchResponse = null;
        try {
            searchResponse = elasticsearchTemplate.search(searchRequest);
            log.info("集中式搜索找房-查询语句:{}", searchRequest.source().toString());

        } catch (Exception e) {
            log.error("HomeServiceImpl -> concentratedFindHouse异常: ", e);
            throw new RuntimeException(e);
        }
        List<ConcentratedFindHouseRespVO> respVOS = new ArrayList<>();

        ParsedCardinality cardinality = (ParsedCardinality) searchResponse.getAggregations().getAsMap().get("bucketCount");
        long count = cardinality.getValue();

        ParsedTerms apartmentGroupTerms = (ParsedTerms) searchResponse.getAggregations().getAsMap().get("apartmentGroup");

        List<Long> apartmentIds = new ArrayList<>();
        apartmentGroupTerms.getBuckets().stream().forEach(apartmentGroupTermsBucket -> {
            ParsedTopHits apartmentTopHits = (ParsedTopHits) apartmentGroupTermsBucket.getAggregations().getAsMap().get("apartmentInfo");
            List<Long> collect = Arrays.stream(apartmentTopHits.getHits().getHits()).map(p -> {
                return JsonUtils.parseObject(p.getSourceAsString(), RoomelEntity.class).getApartmentId();
            }).collect(Collectors.toList());
            apartmentIds.addAll(collect);
        });

        Map<Long, Apartment> apartmentMap = new HashMap<>();
        Map<Long, List<RoomLayout>> roomlayoutMap = new HashMap<>();
        Map<Long, Boolean> isHasEmptyMap = new HashMap<>();
        if (ObjectUtil.isNotEmpty(apartmentIds)) {
            List<Apartment> apartments = apartmentRepository.listByIds(apartmentIds);
            apartmentMap = apartments.stream().collect(Collectors.toMap(Apartment::getId, o -> o));
            List<RoomLayout> roomLayoutList = roomLayoutRepository.getByApartmentIds(apartmentIds);
            roomlayoutMap = roomLayoutList.stream().collect(Collectors.groupingBy(RoomLayout::getApartmentId));
            List<Long> roomLayoutIds = roomLayoutList.stream().map(RoomLayout::getId).collect(Collectors.toList());
            isHasEmptyMap = roomHouseRepository.isHasEmptyRoomByIds(roomLayoutIds);
        }


        Map<Long, Apartment> finalApartmentMap = apartmentMap;
        Map<Long, List<RoomLayout>> finalRoomlayoutMap = roomlayoutMap;
        Map<Long, Boolean> finalIsHasEmptyMap = isHasEmptyMap;
        apartmentGroupTerms.getBuckets().forEach(apartmentGroupTermsBucket -> {
            ParsedMin parsedMin = (ParsedMin) apartmentGroupTermsBucket.getAggregations().getAsMap().get("priceMin");
            ParsedMax parsedMax = (ParsedMax) apartmentGroupTermsBucket.getAggregations().getAsMap().get("spaceMax");
            double price = Double.isInfinite(parsedMin.getValue()) ? 0 : parsedMin.getValue();
            double space = parsedMax.getValue();
            ParsedTopHits apartmentTopHits = (ParsedTopHits) apartmentGroupTermsBucket.getAggregations().getAsMap().get("apartmentInfo");
            for (SearchHit hit : apartmentTopHits.getHits().getHits()) {
                RoomelEntity roomelEntity = JsonUtils.parseObject(hit.getSourceAsString(), RoomelEntity.class);
                log.info("集中式搜索找房 - 找到房源:{}", hit.getSourceAsString());
                Apartment apartment = finalApartmentMap.get(roomelEntity.getApartmentId());
                if (apartment == null) {
                    continue;
                }
                String picUrl = apartment.getPicUrl();
                if (StrUtil.isNotEmpty(picUrl)) {
                    picUrl = picUrl.split(",")[0];
                }
                //门店下房型的数据
                List<RoomLayout> roomLayouts = finalRoomlayoutMap.get(roomelEntity.getApartmentId());
                List<ConcentratedFindHouseRespVO.LayoutInfo> layoutInfos = new ArrayList<>();
                roomLayouts.forEach(roomLayout -> {
                    ConcentratedFindHouseRespVO.LayoutInfo layoutInfo = new ConcentratedFindHouseRespVO.LayoutInfo();
                    layoutInfo.setLayoutId(roomLayout.getId());
                    if (StrUtil.isNotEmpty(roomLayout.getPicUrl())) {
                        String[] split = roomLayout.getPicUrl().split(",");
                        layoutInfo.setPicUrl(split[0]);
                    }
                    layoutInfo.setName(roomLayout.getName());
                    layoutInfo.setPrice(roomLayout.getMonthRental());
                    // 该房型是否还有空房
                    layoutInfo.setIsFullRoom(finalIsHasEmptyMap.containsKey(roomLayout.getId()) ? finalIsHasEmptyMap.get(roomLayout.getId()) : false);
                    layoutInfos.add(layoutInfo);
                });
                // 距离
                Double mater = null;
                if (StrUtil.isAllNotEmpty(lon, lat)) {
                    String origins = lon + "," + lat;
                    String destination = "";
                    if (StrUtil.isNotEmpty(roomelEntity.getApartmentGeo())) {
                        String[] split = roomelEntity.getApartmentGeo().split(",");
                        destination = split[1] + "," + split[0];
                    }
                    mater = baiDuMapUtil.walkingRoutePlanning(origins, destination, BaiDuMapUtil.WALKING, BaiDuMapUtil.DISTANCE);
                }
                ConcentratedFindHouseRespVO respVO = ConcentratedFindHouseRespVO.builder()
                        .aptId(roomelEntity.getApartmentId())
                        .aptName(apartment.getName())
                        .address(apartment.getAddress())
                        .storeTel(apartment.getStoreTel())
                        .monthRental(BigDecimal.valueOf(price).setScale(2, BigDecimal.ROUND_HALF_DOWN).toString())
                        .space(space)
                        .picUrl(picUrl)
                        .layoutInfo(layoutInfos)
                        .distance(mater)
                        .tags(StrUtil.isNotEmpty(apartment.getTags()) ? Arrays.asList(apartment.getTags().split(",")) : new ArrayList<>())
                        .build();
                respVOS.add(respVO);
            }
        });
        return CommonResult.success(new PageResult<>(respVOS, count));
    }

    /**
     * 分散式-搜索找房
     *
     * @param searchBox        搜索框查询
     * @param district         区域
     * @param monthRentalMin   租金范围-下限
     * @param monthRentalMax   租金范围-上限
     * @param longitude        地铁站经度
     * @param latitude         地铁站纬度
     * @param sortRule         排序规则 1:推荐排序 2:最新发布 3:价格升序 4:价格降序 5:面积升序 6:面积降序
     * @param isWhole          出租方式,是否整租 true:整租 false:合租
     * @param bedRoomNum       户型-几室 1:一室 2:二室 3:三室 4:四室 5:其他
     * @param publicFacilities 特色需求(公共设施)
     * @param pageNum
     * @param pageSize
     * @param tenantId
     * @return
     */
    @Override
    public CommonResult<PageResult<NoConcentratedFindHouseRespVO>> noConcentratedFindHouse(String searchBox, String district, Double monthRentalMin, Double monthRentalMax, Double longitude, Double latitude, Integer sortRule, Boolean isWhole, Integer bedRoomNum, String publicFacilities, Integer pageNum, Integer pageSize, Long tenantId, Long roomAreaId) {
        // 原生查询请求
        SearchRequest searchRequest = new SearchRequest(ApiConstants.ROOMELENTIRY);
        // 查询条件构建
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery()
                .must(QueryBuilders.termQuery("tenantId", tenantId))
                .must(QueryBuilders.termQuery("isConcentrated", false))
                .must(QueryBuilders.termQuery("isLock", false))
                .must(QueryBuilders.termQuery(IsStop, false))
                .must(QueryBuilders.termQuery("houseState.keyword", "1"))
                .mustNot(QueryBuilders.termQuery("roomType.keyword", RoomTypeEnum.GGQY.getValue()))
                .must(QueryBuilders.existsQuery("price"));
        // 分散式只查询整租 or 合租的子房间
        BoolQueryBuilder boolQueryBuilderA = new BoolQueryBuilder();
        boolQueryBuilderA.should(QueryBuilders.termQuery("isWhole", true));
        BoolQueryBuilder boolQueryBuilder2 = new BoolQueryBuilder();

        BoolQueryBuilder boolQueryBuilder3 = new BoolQueryBuilder();
        boolQueryBuilder3.must(QueryBuilders.termQuery("isWhole", false));
        boolQueryBuilder3.must(QueryBuilders.existsQuery("superId"));
        boolQueryBuilder2.must(boolQueryBuilder3);
        boolQueryBuilderA.should(boolQueryBuilder2);

        boolQueryBuilder.must(boolQueryBuilderA);
        // 搜索框查询
        if (ObjectUtil.isNotEmpty(searchBox)) {
            BoolQueryBuilder shouldQueryBuilder = QueryBuilders.boolQuery()
                    .should(QueryBuilders.wildcardQuery("district.keyword", "*" + searchBox + "*"))
                    .should(QueryBuilders.wildcardQuery("street.keyword", "*" + searchBox + "*"))
                    .should(QueryBuilders.multiMatchQuery(searchBox, "apartmentName", "apartmentAreaName", "address").analyzer(Analyzer.ik_max_word.name()));
            boolQueryBuilder.must(shouldQueryBuilder);
        }
        if (ObjectUtil.isNotEmpty(roomAreaId)) {
            boolQueryBuilder.must(QueryBuilders.termQuery("roomAreaId", roomAreaId));
        }
        // 区域
        if (ObjectUtil.isNotEmpty(district)) {
            boolQueryBuilder.must(QueryBuilders.termQuery("district", district));
        }

        // 价格区间
        if (ObjectUtil.isAllNotEmpty(monthRentalMin, monthRentalMax)) {
            boolQueryBuilder.must(QueryBuilders.rangeQuery("price").gte(monthRentalMin).lte(monthRentalMax));
        } else if (ObjectUtil.isNotEmpty(monthRentalMin) && ObjectUtil.isEmpty(monthRentalMax)) {
            boolQueryBuilder.must(QueryBuilders.rangeQuery("price").gte(monthRentalMin));
        }

        // 地理位置
        if (ObjectUtil.isAllNotEmpty(longitude, latitude)) {
            boolQueryBuilder.must(QueryBuilders.geoDistanceQuery("apartmentGeo").distance("2000").point(latitude, longitude));
        }

        // 出租方式
        if (ObjectUtil.isNotEmpty(isWhole)) {
            boolQueryBuilder.must(QueryBuilders.termQuery("isWhole", isWhole));
        }

        // 几室
        if (ObjectUtil.isNotEmpty(bedRoomNum)) {
            boolQueryBuilder.must(QueryBuilders.termQuery("bedroomNum", bedRoomNum));
        }

        // 特色需求
        if (ObjectUtil.isNotEmpty(publicFacilities)) {
            String[] split = publicFacilities.split(",");
            BoolQueryBuilder shouldBoolQueryBuilder = QueryBuilders.boolQuery();
            for (String s : split) {
                shouldBoolQueryBuilder.should(QueryBuilders.matchQuery("publicFacilities", s));
            }
            boolQueryBuilder.must(shouldBoolQueryBuilder);
        }

        // 排序规则
        if (SortRuleEnum.TJ.getValue().equals(sortRule)) {
            // 推荐排序， 分散式不做推荐排序
        } else if (SortRuleEnum.ZX.getValue().equals(sortRule)) {
            // 按最新发布房源排序
            searchSourceBuilder.sort("createTime", SortOrder.DESC);
        } else if (SortRuleEnum.JGSX.getValue().equals(sortRule)) {
            // 房源价格升序
            searchSourceBuilder.sort("price", SortOrder.ASC);
        } else if (SortRuleEnum.JGJX.getValue().equals(sortRule)) {
            // 房源价格降序
            searchSourceBuilder.sort("price", SortOrder.DESC);
        } else if (SortRuleEnum.MJSX.getValue().equals(sortRule)) {
            // 房源面积升序
            searchSourceBuilder.sort("space", SortOrder.ASC);
        } else if (SortRuleEnum.MJJX.getValue().equals(sortRule)) {
            // 房源面积降序
            searchSourceBuilder.sort("space", SortOrder.DESC);
        }
        // 查询条件
        searchSourceBuilder.query(boolQueryBuilder);
        searchSourceBuilder.from((pageNum - 1) * pageSize);
        searchSourceBuilder.size(pageSize);
        if (((pageNum - 1) * pageSize) + pageSize > 10000) {
            throw new ServiceException(new ErrorCode(20010, "请根据条件筛选，缩小查询范围"));
        }
        searchRequest.source(searchSourceBuilder);

        SearchResponse searchResponse = null;
        try {
            searchResponse = elasticsearchTemplate.search(searchRequest);
            log.info("分散式搜索找房-查询语句:{}", searchRequest.source().toString());
        } catch (Exception e) {
            log.error("HomeServiceImpl -> noConcentratedFindHouse异常: ", e);
            throw new RuntimeException(e);
        }
        SearchHits hits = searchResponse.getHits();
        long count = hits.getTotalHits().value;
        List<NoConcentratedFindHouseRespVO> respVOS = new ArrayList<>();
        for (SearchHit hit : hits) {
            RoomelEntity roomelEntity = JsonUtils.parseObject(hit.getSourceAsString(), RoomelEntity.class);
            log.info("分散式搜索找房 - 找到房源:{}", hit.getSourceAsString());

            RoomHouse roomHouse = roomHouseRepository.myGetById(roomelEntity.getId());

            // 拼接地址
            StringBuilder sb = new StringBuilder();
            sb.append(roomHouse.getProvince());
            sb.append(roomHouse.getCity());
            sb.append(roomHouse.getDistrict());
            sb.append(roomHouse.getStreet());
            sb.append(roomHouse.getAddress());

            // 取第一张图作为主图
            String picUrl = roomHouse.getPicUrl();
            if (StrUtil.isNotEmpty(picUrl)) {
                picUrl = picUrl.split(",")[0];
            }
            RoomApartmentArea roomApartmentArea = roomApartmentAreaRepository.myGetById(roomHouse.getApartmentAreaId());

            NoConcentratedFindHouseRespVO respVO = new NoConcentratedFindHouseRespVO()
                    .setRoomHouseId(roomelEntity.getId().toString())
                    .setName(roomelEntity.getFullName())
                    .setRoomHouseTypeName(roomelEntity.getRoomHouseTypeName())
                    .setSpace(roomelEntity.getSpace())
                    .setToWard(StrUtil.isNotEmpty(roomHouse.getToward()) ? TowardEnum.getNameByValue(roomHouse.getToward()) : "")
                    .setRoomType(StrUtil.isNotEmpty(roomHouse.getRoomType()) ? RoomTypeEnum.getNameByValue(roomHouse.getRoomType()) : "")
                    .setIsWhole(roomHouse.getIsWhole())
                    .setAddress(sb.toString())
                    .setPrice(roomHouse.getMonthRental())
                    .setFloorNum(roomApartmentArea.getFloorNum())
                    .setInFloorNum(roomHouse.getFloorNo())
                    .setPicUrl(picUrl);
            respVOS.add(respVO);
        }

        return CommonResult.success(new PageResult<>(respVOS, count));
    }

    /**
     * 社区-查看门店详情
     *
     * @param apartmentId
     * @return
     */
    @Override
    public CommonResult<GetApartmentInfoRespVO> getApartmentInfo(Long apartmentId) {
        // 获取门店信息
        Apartment apartment = apartmentRepository.myGetById(apartmentId);
        // 门店标签
        String tags = apartment.getTags();
        List<String> apartmentTags = null;
        if (StrUtil.isNotEmpty(tags)) {
            apartmentTags = Arrays.asList(tags.split(","));
        }

        // 门店下的所有房型
        List<RoomLayout> roomLayouts = roomLayoutRepository.getByApartmentId(apartmentId);
        List<LayoutInfo> layoutInfos = this.assembleLayoutInfo(apartmentId, roomLayouts);
        // 房型按价格从低到高排序
        layoutInfos = layoutInfos.stream().sorted(Comparator.comparing(LayoutInfo::getPrice, Comparator.nullsLast(Double::compareTo))).collect(Collectors.toList());
        // 房间配置
        String publicFacilities = apartment.getPublicFacilities();
        List<String> facilities = null;
        if (StrUtil.isNotEmpty(publicFacilities)) {
            facilities = Arrays.asList(publicFacilities.split(","));
        }
        // 地址
        StringBuilder sb = new StringBuilder();
        sb.append(apartment.getProvince());
        sb.append(apartment.getCity());
        sb.append(apartment.getDistrict());
//        sb.append(apartment.getStreet());
        sb.append(apartment.getAddress());

        // 价格有小数，保留两位小数；价格无小数位，不需保留小数，取整数；未有价格的，显示0元/月
        String price = "0";
/*        if (ObjectUtil.isNotEmpty(layoutInfos.get(0).getPrice())) {
            Double priced = layoutInfos.get(0).getPrice();
            price = NumberUtils.format(priced);
        }*/

        // 门店下租金最低的房源
        List<RoomHouse> roomHouses = roomHouseRepository.getByApartmentIdOrderByMonthRental(apartmentId);
        if (ObjectUtil.isNotEmpty(roomHouses)) {
            RoomHouse roomHouse = roomHouses.get(0);
            price = NumberUtils.format(roomHouse.getMonthRental());
        }

        ApartmentInfo apartmentInfo = new ApartmentInfo()
                .setPicUrl(ObjectUtil.isNotEmpty(apartment.getPicUrl()) ? Arrays.asList(apartment.getPicUrl().split(",")) : new ArrayList<String>())
                .setTags(apartmentTags)
                .setApartmentName(apartment.getName())
                .setAddress(sb.toString())
                .setDiscountInfo(apartment.getDiscountInfo())
                .setDiscountTitle(apartment.getDiscountTitle())
                .setStoreTel(apartment.getStoreTel())
                .setRoomAssets(apartment.getRoomAssets())
                .setTransportLine(apartment.getTransportLine())
                // 门店价格取房型的最低价格
                .setPrice(price)
                .setPublicFacilities(facilities)
                .setQrCodeUrl(apartment.getQrCodeUrl())
                .setVrPicUrl(apartment.getVrPicUrl())
                .setVrUrl(apartment.getVrUrl())
                .setVideoUrl(apartment.getVideoUrl())
                .setLon(apartment.getLongitude())
                .setLat(apartment.getLatitude());
        GetApartmentInfoRespVO respVO = new GetApartmentInfoRespVO()
                .setApartmentInfo(apartmentInfo)
                .setLayoutInfos(layoutInfos);

        return CommonResult.success(respVO);
    }

    private List<LayoutInfo> assembleLayoutInfo(Long apartmentId, List<RoomLayout> roomLayouts) {
        List<LayoutInfo> layoutInfos = new ArrayList<>();
        roomLayouts.stream().forEach(roomLayout -> {
            String roomLayoutPicUrl = roomLayout.getPicUrl();
            if (StrUtil.isNotEmpty(roomLayoutPicUrl)) {
                roomLayoutPicUrl = roomLayoutPicUrl.split(",")[0];
            }
            // 房型所属的户型信息
            RoomHouseType roomHouseType = roomHouseTypeRepository.myGetById(roomLayout.getRoomHouseTypeId());
            // 房型下的所有空房源
            List<RoomHouse> emptyRoomHouses = roomHouseRepository.getEmptyRoomByLayoutId(roomLayout.getId());
            boolean isFullRoom = false;
            // 没有空房源，说明满房
            if (ObjectUtil.isEmpty(emptyRoomHouses)) {
                isFullRoom = true;
            }
            LayoutInfo layoutInfo = new LayoutInfo()
                    .setLayoutId(roomLayout.getId())
                    .setLayoutName(roomLayout.getName())
                    .setHouseTypeName(roomHouseType.getName())
                    .setPrice(roomLayout.getMonthRental())
                    .setSpaceMin(roomLayout.getSpaceMin())
                    .setSpaceMax(roomLayout.getSpaceMax())
                    .setIsFullRoom(isFullRoom)
                    .setPicUrl(roomLayoutPicUrl);
            layoutInfos.add(layoutInfo);
        });
        return layoutInfos;
    }

    /**
     * 社区-查看房型详情
     *
     * @param apartmentId 门店id
     * @param layoutId    房型id
     * @return
     */
    @Override
    public CommonResult<GetLayoutInfoRespVO> getLayoutInfo(Long apartmentId, Long layoutId) {
        RoomLayout roomLayout = roomLayoutRepository.myGetById(layoutId);
        String picUrl = roomLayout.getPicUrl();
        // 图片
        List<String> picUrls = Arrays.asList(picUrl.split(","));
        // vr封面
        String vrPicUrl = roomLayout.getVrPicUrl();
        // vr链接
        String vrUrl = roomLayout.getVrUrl();
        // 房型名
        String layoutName = roomLayout.getName();
        // 户型
        RoomHouseType roomHouseType = roomHouseTypeRepository.myGetById(roomLayout.getRoomHouseTypeId());

        // 门店标签
        Apartment apartment = apartmentRepository.myGetById(apartmentId);
        List<String> tags = new ArrayList<>();
        if (StrUtil.isNotEmpty(apartment.getTags())) {
            tags = Arrays.asList(apartment.getTags().split(","));
        }
        // 房间配置
        String detailPoint = roomLayout.getDetailPoint();
        List<String> detailPoints = new ArrayList<>();
        if (StrUtil.isNotEmpty(detailPoint)) {
            detailPoints = Arrays.asList(detailPoint.split(","));
        }
        StringBuilder sb = new StringBuilder();
        sb.append(apartment.getProvince());
        sb.append(apartment.getCity());
        sb.append(apartment.getDistrict());
        sb.append(apartment.getStreet());
        sb.append(apartment.getAddress());
        // 该房型是否还有空房
        List<RoomHouse> rooms = roomHouseRepository.getEmptyRoomByLayoutId(roomLayout.getId());
        // 该门店下其他的房型
        List<RoomLayout> roomLayouts = roomLayoutRepository.getByApartmentIdAndFilter(apartmentId, layoutId);
        // 组装房型信息
        List<LayoutInfo> layoutInfos = this.assembleLayoutInfo(apartmentId, roomLayouts);
        // 门店图片
        String apartmentPicUrl = "";
        if (StrUtil.isNotEmpty(apartment.getPicUrl())) {
            apartmentPicUrl = apartment.getPicUrl().split(",")[0];
        }

        GetLayoutInfoRespVO respVO = new GetLayoutInfoRespVO()
                .setRoomHouseTypeName(roomHouseType.getName())
                .setPeopleNumber(roomLayout.getPeopleNumber())
                .setIsFullRoom(ObjectUtil.isEmpty(rooms))
                .setPicUrls(picUrls)
                .setVrPicUrl(vrPicUrl)
                .setVrUrl(vrUrl)
                .setVideoUrl(roomLayout.getVideoUrl())
                .setLayoutName(layoutName)
                .setLayoutId(layoutId)
                .setApartmentId(apartmentId)
                .setApartmentName(apartment.getName())
                .setApartmentPicUrl(apartmentPicUrl)
                .setPrice(roomLayout.getMonthRental())
                .setTags(tags)
                .setDetailPoints(detailPoints)
                .setAddress(sb.toString())
                .setSpaceMin(roomLayout.getSpaceMin())
                .setSpaceMax(roomLayout.getSpaceMax())
                .setStoreTel(apartment.getStoreTel())
                .setLat(apartment.getLatitude())
                .setLon(apartment.getLongitude())
                .setLayoutInfos(layoutInfos);
        return CommonResult.success(respVO);
    }

    /**
     * 社区-房源详情
     *
     * @param roomHouseId 房源id
     * @return
     */
    @Override
    public CommonResult<GetRoomHouseInfoRespVO> getRoomHouseInfo(Long roomHouseId) {
        RoomHouse roomHouse = roomHouseRepository.myGetById(roomHouseId);

        List<String> picUrls = new ArrayList<>();
        if (ObjectUtil.isNotEmpty(roomHouse.getPicUrl())) {
            picUrls = Arrays.asList(roomHouse.getPicUrl().split(","));
        }

        List<String> publicFacilitie = new ArrayList<>();
        if (ObjectUtil.isNotEmpty(roomHouse.getPublicFacilities())) {
            publicFacilitie = Arrays.asList(roomHouse.getPublicFacilities().split(","));
        }
        if (ObjectUtil.isNotEmpty(roomHouse.getSuperId())) {
            RoomHouse paremtRoomHouse = roomHouseRepository.myGetById(roomHouse.getSuperId());
            if (ObjectUtil.isNotEmpty(paremtRoomHouse.getPublicFacilities())) {
                publicFacilitie = Arrays.asList(paremtRoomHouse.getPublicFacilities().split(","));
            }
        }

        // 取门店标签
        Apartment apartment = apartmentRepository.myGetById(roomHouse.getApartmentId());

        List<String> tags = new ArrayList<>();
        if (ObjectUtil.isNotEmpty(apartment.getTags())) {
            tags = Arrays.asList(apartment.getTags().split(","));
        }
        RoomHouseFilings roomHouseFilings = roomHouseFilingsRepository.getByRoomHouseId(roomHouseId);
        RoomHouseType roomHouseType = roomHouseTypeRepository.myGetById(roomHouse.getRoomHouseTypeId());
        RoomApartmentArea roomApartmentArea = roomApartmentAreaRepository.getById(roomHouse.getApartmentAreaId());

        StringBuilder sb = new StringBuilder();
        sb.append(roomHouse.getProvince());
        sb.append(roomHouse.getCity());
        sb.append(roomHouse.getDistrict());
        sb.append(roomHouse.getStreet());
        sb.append(roomHouse.getAddress());

        GetRoomHouseInfoRespVO respVO = new GetRoomHouseInfoRespVO()
                .setPicUrls(picUrls)
                .setVrPicUrl(roomHouse.getVrPicUrl())
                .setVrUrl(roomHouse.getVrUrl())
                .setRoomHouseName(roomHouse.getName())
                .setRoomFullName(RoomHouseUtil.getHouseFullName(roomHouse))
                .setPublicFacilities(publicFacilitie)
                .setTags(tags)
                .setStoreTel(apartment.getStoreTel())
                .setSpace(roomHouse.getSpace().toString())
                .setPrice(roomHouse.getMonthRental())
                .setAddress(sb.toString())
                .setLat(roomApartmentArea.getLatitude())
                .setLon(roomApartmentArea.getLongitude())
                .setIsConcentrated(roomHouse.getIsConcentrated())
                .setIsWhole(roomHouse.getIsWhole())
                .setRoomHouseTypeName(roomHouseType.getName())
                .setToward(StrUtil.isNotEmpty(roomHouse.getToward()) ? TowardEnum.getNameByValue(roomHouse.getToward()) : "")
                .setPeopleNum(roomHouse.getPeopleNum())
                .setRoomHouseFilingsStatus(ObjectUtil.isNotEmpty(roomHouseFilings) ? roomHouseFilings.getStatus() : null)
                .setCheckCode(ObjectUtil.isNotEmpty(roomHouseFilings) ? roomHouseFilings.getCheckCode() : null)
                .setApartmentId(roomHouse.getApartmentId())
                .setRoomType(ObjectUtil.isNotEmpty(roomHouse.getRoomType()) ? RoomTypeEnum.getNameByValue(roomHouse.getRoomType()) : "");
        return CommonResult.success(respVO);
    }

    /**
     * 社区-搜索房源
     *
     * @param isConcentrated 是否集中式 true:集中式 false:分散式 null:全部
     * @param searchBox      搜索框内容
     * @param pageNo
     * @param pageSize
     * @param tenantId       租户ID
     * @return
     */
    @Override
    public CommonResult<PageResult<SearchRoomRespVo>> searchRoom(Boolean isConcentrated, String searchBox, Integer pageNo, Integer pageSize, Long tenantId) {
        // 按小区名模糊搜索, 对结果按门店分组, 分页
        SearchRequest searchRequest = new SearchRequest(ApiConstants.ROOMELENTIRY);
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery()
                .must(QueryBuilders.termQuery("tenantId", tenantId))
                .must(QueryBuilders.termQuery("isLock", false))
                .must(QueryBuilders.termQuery(IsStop, false))
                .must(QueryBuilders.termQuery("houseState.keyword", "1"))
                .must(QueryBuilders.existsQuery("price"));

        // 构建查询条件
        this.buildQueryCriteria(isConcentrated, searchBox, queryBuilder);
        // 构建分组条件
        TermsAggregationBuilder group = null;
        if (isConcentrated) {
            group = AggregationBuilders.terms("group").field("apartmentId").order(BucketOrder.key(true)).size(10000);
        } else {
            group = AggregationBuilders.terms("group").field("roomAreaId").order(BucketOrder.key(true)).size(10000);
        }
        TopHitsAggregationBuilder top = AggregationBuilders.topHits("top").fetchSource(new String[]{"apartmentId", "apartmentName", "isConcentrated", "createTime", "roomAreaId", "apartmentAreaName"}, null).size(1);
        MinAggregationBuilder priceMinAgg = AggregationBuilders.min("priceMin").field("price");
        group.subAggregation(priceMinAgg);
        // 桶排序，并对桶分页
        List<FieldSortBuilder> sortList = new ArrayList<>();
        // 获取分组后的桶数量
        CardinalityAggregationBuilder cardinalityAggregationBuilder = null;
        if (isConcentrated) {
            cardinalityAggregationBuilder = AggregationBuilders.cardinality("bucketCount").field("apartmentId");
        } else {
            cardinalityAggregationBuilder = AggregationBuilders.cardinality("bucketCount").field("roomAreaId");
        }
        // 按最新发布房源排序
        top.sort("createTime", SortOrder.DESC);
        group.subAggregation(top);
        MaxAggregationBuilder maxAggregationBuilder = AggregationBuilders.max("maxSort").field("createTime");
        group.subAggregation(maxAggregationBuilder);
        sortList.add(new FieldSortBuilder("maxSort").order(SortOrder.DESC));
        // 桶排序和分页
        BucketSortPipelineAggregationBuilder bucketSort = PipelineAggregatorBuilders.bucketSort("testSort", sortList).from((pageNo - 1) * pageSize).size(pageSize);

        group.subAggregation(bucketSort);
        searchSourceBuilder.query(queryBuilder);
        searchSourceBuilder.aggregation(group);
        searchSourceBuilder.aggregation(cardinalityAggregationBuilder);
        searchRequest.source(searchSourceBuilder);
        // 处理查询结果
        SearchResponse search = null;
        List<SearchRoomRespVo> respVos = new ArrayList<>();
        AtomicLong count = new AtomicLong(0l);
        try {
            search = elasticsearchTemplate.search(searchRequest);
            log.info("社区-搜索房源ES请求:{}", searchRequest.source().toString());
            ParsedCardinality cardinality = (ParsedCardinality) search.getAggregations().asMap().get("bucketCount");
            count.set(cardinality.getValue());
            ParsedTerms apartmentGroupTerms = (ParsedTerms) search.getAggregations().asMap().get("group");
            apartmentGroupTerms.getBuckets().forEach(apartmentGroupTerm -> {
                ParsedMin parsedMin = (ParsedMin) apartmentGroupTerm.getAggregations().getAsMap().get("priceMin");
                double price = Double.isInfinite(parsedMin.getValue()) ? 0 : parsedMin.getValue();
                SearchRoomRespVo respVo = new SearchRoomRespVo();
                respVo.setAvailableForRentNum(apartmentGroupTerm.getDocCount());
                ParsedTopHits apartmentTop = (ParsedTopHits) apartmentGroupTerm.getAggregations().asMap().get("top");
                if (ObjectUtil.isEmpty(apartmentTop.getHits().getHits())) {
                    count.set(0l);
                    return;
                }
                for (SearchHit hit : apartmentTop.getHits().getHits()) {
                    RoomelEntity roomelEntity = JsonUtils.parseObject(hit.getSourceAsString(), RoomelEntity.class);
                    respVo.setId(isConcentrated ? roomelEntity.getApartmentId() : roomelEntity.getRoomAreaId());
                    respVo.setApartmentId(roomelEntity.getApartmentId());
                    respVo.setName(isConcentrated ? roomelEntity.getApartmentName() : roomelEntity.getApartmentAreaName());
                    respVo.setIsConcentrated(roomelEntity.getIsConcentrated());
                    respVo.setPrice(NumberUtils.format(price));
                    respVo.setCreateTime(roomelEntity.getCreateTime());
                }
                respVos.add(respVo);
            });
        } catch (Exception e) {
            log.error("搜索房源异常", e);
        }
        if (CollectionUtil.isNotEmpty(respVos)) {
            List<Long> apartmentIds = respVos.stream().map(SearchRoomRespVo::getApartmentId).collect(Collectors.toList());
            List<Apartment> apartments = apartmentRepository.listByIds(apartmentIds);
            Map<Long, String> apartmentMap = apartments.stream().collect(Collectors.toMap(Apartment::getId, Apartment::getPicUrl, (s1, s2) -> s1));
            respVos.forEach(s -> s.setPicUrl(apartmentMap.get(s.getApartmentId())));
        }
        return CommonResult.success(new PageResult<>(respVos, count.get()));
    }

    /**
     * 同小区房源
     *
     * @param roomHouseId 房源id
     * @param pageNo      分页参数
     * @param pageSize    分页参数
     * @param tenantId    租户id
     * @return
     */
    @Override
    public CommonResult<PageResult<SameResidentialListingsRespVO>> sameResidentialListings(Long roomHouseId, Integer pageNo, Integer pageSize, Long tenantId) {
        RoomHouse roomHouse = roomHouseRepository.myGetById(roomHouseId);
        RoomApartmentArea roomApartmentArea = roomApartmentAreaRepository.myGetById(roomHouse.getApartmentAreaId());
        return roomelEntityRepository.getRoomHouseByAreaIdAndExclude(roomApartmentArea.getRoomAreaId(), roomHouseId, roomHouse.getIsWhole(), pageNo, pageSize, tenantId);
    }

    /**
     * 社区-房源详情-同房源其他房间
     *
     * @param roomHouseId 房源ID
     * @param tenantId    租户ID
     * @return
     */
    @Override
    public CommonResult<List<OtherRoomsInTheSameListingRespVO>> otherRoomsInTheSameListing(Long roomHouseId, Long tenantId) {
        RoomHouse roomHouse = roomHouseRepository.myGetById(roomHouseId);
        return roomelEntityRepository.otherRoomsInTheSameListing(roomHouseId, roomHouse.getApartmentAreaId(), tenantId);
    }

    /**
     * 社区-房源详情-推荐附近房源
     *
     * @param roomHouseId 房源ID
     * @param tenantId    租户ID
     * @return
     */
    @Override
    public CommonResult<List<OtherRoomsInTheSameListingRespVO>> recommendListingsNearYou(Long roomHouseId, Long tenantId) {
        RoomHouse roomHouse = roomHouseRepository.myGetById(roomHouseId);
        RoomApartmentArea roomApartmentArea = roomApartmentAreaRepository.myGetById(roomHouse.getApartmentAreaId());
        return roomelEntityRepository.recommendListingsNearYou(roomApartmentArea.getLongitude(), roomApartmentArea.getLatitude(), roomHouseId, tenantId);
    }

    /**
     * 查询房源房型
     * @return
     */
    @Override
    public CommonResult<List<RoomLayoutVO>> queryRoomLayout() {
        return roomHouseRepository.queryRoomLayout();
    }

    private void buildQueryCriteria(Boolean isConcentrated, String searchBox, BoolQueryBuilder queryBuilder) {
        if (ObjectUtil.isNotEmpty(searchBox)) {
            // 小区名模糊搜索
            queryBuilder.must(QueryBuilders.wildcardQuery("apartmentAreaName.keyword", "*" + searchBox + "*"));
        }
        if (ObjectUtil.isNotEmpty(isConcentrated) && isConcentrated) {
            QueryBuilder queryBuilder10 = QueryBuilders.termQuery("isConcentrated", true);
            queryBuilder.must(queryBuilder10);
        } else if (ObjectUtil.isNotEmpty(isConcentrated) && !isConcentrated) {
            QueryBuilder queryBuilder10 = QueryBuilders.termQuery("isConcentrated", false);
            queryBuilder.must(queryBuilder10);
            // 分散式只查询整租 or 合租的子房间
            BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
            boolQueryBuilder.should(QueryBuilders.termQuery("isWhole", true));
            BoolQueryBuilder boolQueryBuilder2 = new BoolQueryBuilder();

            BoolQueryBuilder boolQueryBuilder3 = new BoolQueryBuilder();
            boolQueryBuilder3.must(QueryBuilders.termQuery("isWhole", false));
            boolQueryBuilder3.must(QueryBuilders.existsQuery("superId"));
            boolQueryBuilder2.must(boolQueryBuilder3);

            BoolQueryBuilder boolQueryBuilder5 = new BoolQueryBuilder();
            boolQueryBuilder5.mustNot(QueryBuilders.termQuery("roomType.keyword", RoomTypeEnum.GGQY.getValue()));
            boolQueryBuilder2.must(boolQueryBuilder5);
            boolQueryBuilder.should(boolQueryBuilder2);

            queryBuilder.must(boolQueryBuilder);
        } else if (ObjectUtil.isEmpty(isConcentrated)) {
            // 查询整租 or 合租的子房间
            BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
            boolQueryBuilder.should(QueryBuilders.termQuery("isWhole", true));
            BoolQueryBuilder boolQueryBuilder2 = new BoolQueryBuilder();

            BoolQueryBuilder boolQueryBuilder3 = new BoolQueryBuilder();
            boolQueryBuilder3.must(QueryBuilders.termQuery("isWhole", false));
            boolQueryBuilder3.must(QueryBuilders.existsQuery("superId"));
            boolQueryBuilder2.must(boolQueryBuilder3);

            BoolQueryBuilder boolQueryBuilder5 = new BoolQueryBuilder();
            boolQueryBuilder5.mustNot(QueryBuilders.termQuery("roomType.keyword", RoomTypeEnum.GGQY.getValue()));
            boolQueryBuilder2.must(boolQueryBuilder5);
            boolQueryBuilder.should(boolQueryBuilder2);

            queryBuilder.must(boolQueryBuilder);
        }
    }
}
